package design.pattern.application;

import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @program JavaBase
 * @description: 工厂方法模式 + 策略模式
 *  迷你版的交易系统
 * @author: zhanglu
 * @create: 2020-05-23 19:03:00
 */
public class FactoryAndStrategyDemo {

    public static void main(String[] args) {
        Card card = initIC();
        //显示一下卡内信息
        System.out.println("========初始卡信息:========="); showCard(card);
        //是否停止运行标志
        boolean flag = true;
        while(flag){
            Trade trade = createTrade();
            DeductionFacade.deduct(card, trade);
            //交易成功，打印出成功处理消息
            System.out.println("\n======交易凭证========");
            System.out.println(trade.getTradeNo()+" 交易成功!");
            System.out.println("本次发生的交易金额为:"+
            trade.getAmount()/100.0+"元");
            //展示一下卡内信息
            showCard(card); System.out.print("\n是否需要退出?(Y/N)");
            if(getInput().equalsIgnoreCase("y")){
                flag = false; //退出
            }

        }
    }

    //初始化一个IC卡
    private static Card initIC(){
        Card card = new Card();
        card.setCardNo("1000101201").setFreeMoney(100000).setSteadyMoney(80000);
        return card;
    }
    //产生一条交易
    private static Trade createTrade(){
        Trade trade = new Trade();
        System.out.println("请输入交易编号：");
        trade.setTradeNo(getInput());
        System.out.println("请输入交易金额：");
        trade.setAmount(Integer.parseInt(getInput()));
        return trade;
    }
    //打印出当前卡内交易金额
    public static void showCard(Card card){
        System.out.println("IC卡编号:" + card.getCardNo());
        System.out.println("固定类型余额:"+ card.getSteadyMoney()/100.0 + " 元");
        System.out.println("自由类型余额:"+ card.getFreeMoney()/100.0 + " 元");
    }
    //获得键盘输入
    public static String getInput(){
        String str = null;
        try {
            str = new BufferedReader(new InputStreamReader(System.in)).readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return str;
    }

}
@Data
@Accessors(chain = true)
class Card{
    //IC卡号码
    private String cardNo;
    //卡内的固定交易金额
    private int steadyMoney;
    //卡内自由交易金额
    private int freeMoney;
}
@Data
@Accessors(chain = true)
class Trade{
    //交易编号
    private String tradeNo;
    //交易金额
    private int amount;
}
//扣款策略接口
interface IDeduction{
    //扣款，提供交易和卡信息，进行扣款，并返回扣款是否成功
    boolean exec(Card card, Trade trade);
}
//扣款策略1
class SteadyDeduction implements IDeduction{
    //固定性交易扣款
    @Override
    public boolean exec(Card card, Trade trade) {
        int halfMoney = (int)Math.rint(trade.getAmount() / 2.0);
        card.setFreeMoney(card.getFreeMoney() - halfMoney);
        card.setSteadyMoney(card.getSteadyMoney() - halfMoney);
        return true;
    }
}
//扣款策略2
class FreeDeduction implements IDeduction{
    //自由扣款
    @Override
    public boolean exec(Card card, Trade trade) {
        //直接从自由余额中扣除
        card.setFreeMoney(card.getFreeMoney() - trade.getAmount());
        return true;
    }
}
//扣款策略封装
class DeductionContext{
    //扣款策略
    private IDeduction iDeduction;
    public DeductionContext(IDeduction iDeduction){
        this.iDeduction = iDeduction;
    }
    //执行扣款
    public boolean exec(Card card, Trade trade){
        return this.iDeduction.exec(card, trade);
    }
}
//策略枚举
@Getter
enum StrategyEnum{
    SteadyyDeduction("design.pattern.application.SteadyDeduction"),
    FreeDeduction("design.pattern.application.FreeDeduction")
    ;

    private String value;
    StrategyEnum(String value) {
        this.value = value;
    }
}
//策略工厂
class StrategyFactory{
    public static IDeduction getDeduction(StrategyEnum strategyEnum){
        IDeduction iDeduction = null;
        try {
            iDeduction = (IDeduction) Class.forName(strategyEnum.getValue()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return iDeduction;
    }
}
//扣款模块封装
class DeductionFacade{
    //对外公布扣款信息
    public static Card deduct(Card card, Trade trade){
        //获得消费策略
        StrategyEnum reg = getDeductionType(trade);
        //初始化一个消费策略对象
        IDeduction deduction = StrategyFactory.getDeduction(reg);
        //产生一个策略上下文
        DeductionContext deductionContext = new DeductionContext(deduction);
        //进行扣款
        deductionContext.exec(card, trade);
        //返回扣款处理完毕后的数据
        return card;
    }
    //获得对应的商户消费策略
    private static StrategyEnum getDeductionType(Trade trade){
        //模拟操作
        if(trade.getTradeNo().contains("abd")){
            return StrategyEnum.FreeDeduction;
        }else {
            return StrategyEnum.SteadyyDeduction;
        }
    }
}

